﻿using System;
using static RIFF.Chunk.FormatEnum;

namespace RIFF.Chunk
{
    /// <summary>
    /// Format Chunk
    /// </summary>
    public class Format : Chunk
    {
        /// <summary>
        /// 表示音频数据的格式。如值为1，表示使用PCM格式。
        /// </summary>
        public short FormatTag;

        /// <summary>
        /// 声道数。值为1则为单声道，为2则是双声道。
        /// </summary>
        public short Channels;

        /// <summary>
        /// 采样率
        /// </summary>
        public int SamplesPerSecond;

        /// <summary>
        /// 音频的码率，每秒播放的字节数。
        /// </summary>
        public int BytesPerSecond;

        /// <summary>
        /// 数据块对齐单位，一次采样的大小，值为声道数 * 量化位数 / 8，在播放时需要一次处理多个该值大小的字节数据。
        /// </summary>
        public short BlockAlign;

        /// <summary>
        /// 音频sample的量化位数，有16位，24位和32位等。
        /// </summary>
        public short BitsPerSample;

        /// <summary>
        /// 扩展区的长度。当WAV文件使用的不是PCM编码方式时，就需要扩展格式块。通常为0或22。
        /// </summary>
        public short ExtDataSize;

        /// <summary>
        /// 扩展块内容。
        /// </summary>
        public byte[]? ExtData;

        /// <summary>
        /// 克隆一个 Chunk，将其解释为 Format Chunk
        /// </summary>
        /// <exception cref="ChunkIdException"/>
        /// <exception cref="ChunkSizeException"/>
        /// <param name="chunk">克隆源</param>
        public Format(Chunk chunk) : base(chunk)
        {
            if (chunk.Id.ToString() != "fmt ")
            {
                throw new ChunkIdException("fmt ", Id.ToString());
            }

            var keepPosition = stream.Position;
            stream.Position = dataPosition;
            var maxPosition = dataPosition + Size;

            var buf16 = new byte[2];
            var buf32 = new byte[4];

            stream.Read(buf16);
            FormatTag = BitConverter.ToInt16(buf16);

            stream.Read(buf16);
            Channels = BitConverter.ToInt16(buf16);

            stream.Read(buf32);
            SamplesPerSecond = BitConverter.ToInt32(buf32);

            stream.Read(buf32);
            BytesPerSecond = BitConverter.ToInt32(buf32);

            stream.Read(buf16);
            BlockAlign = BitConverter.ToInt16(buf16);

            stream.Read(buf16);
            BitsPerSample = BitConverter.ToInt16(buf16);

            if (FormatTag != 1)
            {
                stream.Read(buf16);
                ExtDataSize = BitConverter.ToInt16(buf16);

                if (ExtDataSize > 0)
                {
                    ExtData = new byte[ExtDataSize];
                    stream.Read(ExtData);
                }
            }

            if (stream.Position > maxPosition)
            {
                throw new ChunkSizeException();
            }

            stream.Position = keepPosition;
        }

        /// <summary>
        /// 组合参数->格式枚举
        /// </summary>
        /// <exception cref="FormatOutException"/>
        /// <returns>格式枚举</returns>
        public FormatEnum GetFormat()
        {
            return this switch
            {
                Format { Channels: 1, BitsPerSample: 8 } => FormatMono8,
                Format { Channels: 1, BitsPerSample: 16 } => FormatMono16,
                Format { Channels: 2, BitsPerSample: 8 } => FormatStereo8,
                Format { Channels: 2, BitsPerSample: 16 } => FormatStereo16,
                _ => throw new FormatOutException("格式无法播放"),
            };
        }
    }
}